/**
 * Copyright 2008 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.sapien.util;

import java.util.HashMap;
import java.util.Map;

/**
 * Context Manager, a utility class used to manage a thread local context.  It allows for binding and unbinding objects
 * to and from the thread local context.
 * @author Adam
 * @version $Id: ContextManager.java 25 2009-05-26 04:23:01Z a.ruggles $
 * 
 * Created on Oct 8, 2008 at 8:13:41 PM 
 */
public final class ContextManager {
	/**
	 * The thread local context implemented as a Map.
	 */
	private static final ThreadLocal<Map<Object, Object>> CONTEXT = new ThreadLocal<Map<Object, Object>>();

	/**
	 * Binds an Object to the context with a given class type.
	 * @param <T> The class type.
	 * @param type The type used as the context key.
	 * @param value The value to bind to the context.
	 */
	public static <T> void bind(final Class<T> type, final T value) {
		bind(type.getName(), value);
	}

	/**
	 * Binds an Object to the context with a given key.
	 * @param key The key used to bind the value.
	 * @param value The value to bind to the context.
	 */
	public static void bind(final Object key, final Object value) {
		Map<Object, Object> map = CONTEXT.get();
		if (map == null) {
			map = new HashMap<Object, Object>();
			CONTEXT.set(map);
		}
		map.put(key, value);
	}

	/**
	 * Returns the bound Object in the context.
	 * @param <T> The Object type to return.
	 * @param type The type used as the context key.
	 * @return An Object of type T or null.
	 */
	public static <T> T getBind(final Class<T> type) {
		return type.cast(getBind(type.getName()));
	}

	/**
	 * Returns the bound Object in the context.
	 * @param key The bound key.
	 * @return An Object or null.
	 */
	public static Object getBind(final Object key) {
		final Map<Object, Object> map = CONTEXT.get();
		if (map == null) {
			return null;
		}
		return map.get(key);
	}

	/**
	 * Returns whether the context contains a bound Object for a class type.
	 * @param <T> The class type.
	 * @param type The type used as the context key.
	 * @return true if there is a bound Object in the context, otherwise false.
	 */
	public static <T> boolean isBound(final Class<T> type) {
		return isBound(type.getName());
	}

	/**
	 * Returns whether the context contains a bound Object for a given key.
	 * @param key The context key.
	 * @return true if there is a bound Object in the context, otherwise false.
	 */
	public static boolean isBound(final Object key) {
		final Map<Object, Object> map = CONTEXT.get();
		return (map != null && map.containsKey(key));
	}

	/**
	 * Removes an Object of type T from the context.
	 * @param <T> The class type.
	 * @param type The type used as the context key.
	 * @return The Object of type T that was removed or null.
	 */
	public static <T> T unbind(final Class<T> type) {
		return type.cast(unbind(type.getName()));
	}

	/**
	 * Removes an Object from the context for a given key.
	 * @param key The key used to bind the value.
	 * @return The Object that was removed or null.
	 */
	public static Object unbind(final Object key) {
		final Map<Object, Object> map = CONTEXT.get();
		if (map == null) {
			return null;
		}
		try {
			return map.remove(key);
		} finally {
			if (map.isEmpty()) {
				CONTEXT.remove();
			}
		}
	}

	/**
	 * Private Constructor.
	 */
	private ContextManager() { }
}
